﻿using System;
using System.Configuration;
using System.Collections.Generic;
using System.Drawing;
using BrainTechLLC;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Linq;
using System.IO;
using System.Text;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Runtime.InteropServices;

namespace WpfImageDuplicates
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public static AllMetaData _metaData;
        public static string _metaDataFilePath = @"c:\meta.dat";
        public static FilterCollection _filters;
        public static FilterCollectionViewModel _filterCollection;
        public static DirectoriesToExamineViewModel _dirsViewModel;
        public static Settings _settings;
        public static string _pathToExaminePath = @"c:\paths.dat";
        public static string _settingsPath = @"c:\settings.dat";
        public static string _filterSave = @"c:\filters.dat";

        public static readonly string[] PathsToExamine = new string[] { 
            @"c:\owen\crawled\599", 
            @"c:\owen\crawled\530", 
            @"c:\owen\crawled\531",
            @"c:\owen\crawled\500",
            @"c:\owen\crawled\501",
            @"c:\owen\crawled\532",
            @"c:\owen\crawled\533" 
        };

        protected FileImageCollectionViewModel _viewModel;
        protected CompareImagesWindow _compareWindow;
        protected FullViewerWindow _newWindow;

        protected DupeRunner _dupeRunner = new DupeRunner();

        public Window1()
        {
            System.Windows.Forms.Application.EnableVisualStyles();
            InitializeComponent();
            Loaded += new RoutedEventHandler(Window1_Loaded);
            _settings = Settings.LoadSettings(_settingsPath);

            if (_settings == null)
                _settings = new Settings();

            Settings.Values = _settings;

            if (File.Exists(_metaDataFilePath))
            {
                _metaData = AllMetaData.ReadFromDisk(_metaDataFilePath);
            }
            else
            {
                _metaData = new AllMetaData();
            }
        }

        protected void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            string[] pathsToExamine = DirectoriesToExamine.Load(_pathToExaminePath);

            if (pathsToExamine == null)
            {
                pathsToExamine = PathsToExamine;
            }

            FilterCollection filters = FilterCollection.Load(_filterSave);

            if (filters == null)
            {
                filters = new FilterCollection();
            }

            _filters = filters;
            _filterCollection = new FilterCollectionViewModel(_filters);
            _filterCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(_filterCollection_CollectionChanged);
            _filterCollection.GenerationChanged += new EventHandler(_filterCollection_GenerationChanged);
            _filterCollection.WantFilterThumbnailsChanged += new EventHandler(_filterCollection_WantFilterThumbnailsChanged);
            filterView.FilterCollectionViewModel = _filterCollection;

            DirectoriesToExamine dirs = new DirectoriesToExamine(pathsToExamine);
            _dirsViewModel = new DirectoriesToExamineViewModel(dirs);
            directoriesView.DirectoriesToExamineViewModel = _dirsViewModel;
            _viewModel = new FileImageCollectionViewModel(_dirsViewModel, this.Dispatcher);
            DataContext = _viewModel;
            collectionView.FileImageCollectionViewModel = _viewModel;
            topCommandBar.DataContext = _viewModel;

            Settings.Values.ApplySettings(this);
        }

        void _filterCollection_WantFilterThumbnailsChanged(object sender, EventArgs e)
        {
            this._viewModel.InvalidateAllThumbnails();
        }

        void _filterCollection_GenerationChanged(object sender, EventArgs e)
        {
            if (Settings.Values.WantFilterThumbnails)
            {
                _viewModel.InvalidateAllThumbnails();
                _viewModel.NeedsRefresh = true;
            }
        }

        void _filterCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (Settings.Values.WantFilterThumbnails)
            {
                _viewModel.InvalidateAllThumbnails();
                _viewModel.NeedsRefresh = true;
            }
        }

        protected void RemoveDuplicates(object sender, EventArgs e)
        {
            if (_compareWindow == null)
            {
                _compareWindow = new CompareImagesWindow();
                _compareWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                _compareWindow.WantRemove += new EventHandler<ObjectEventArgs<FileImageViewModel, FileImageViewModel>>(_compareWindow_WantRemove);
                _compareWindow.NoRemove += new EventHandler<ObjectEventArgs<FileImageViewModel, FileImageViewModel>>(_compareWindow_NoRemove);
                _compareWindow.WantRemoveAll += new EventHandler<ObjectEventArgs<FileImageViewModel, FileImageViewModel>>(_compareWindow_WantRemoveAll);
                _compareWindow.ClosedWindow += new EventHandler<ObjectEventArgs<FileImageViewModel, FileImageViewModel>>(_compareWindow_ClosedWindow);
            }

            _dupeRunner.AssignDuplicates(_viewModel.FindDuplicates());
            System.Windows.MessageBox.Show("Found " + _dupeRunner.DupeCount.ToString() + " duplicates");

            _dupeRunner.ShowDupe(_compareWindow);
        }

        void _compareWindow_WantRemoveAll(object sender, ObjectEventArgs<FileImageViewModel, FileImageViewModel> e)
        {
            _dupeRunner.RemoveAll(_viewModel);
            _compareWindow.Close();
        }


        void _compareWindow_NoRemove(object sender, ObjectEventArgs<FileImageViewModel, FileImageViewModel> e)
        {
            _dupeRunner.ShowNextDupe(_compareWindow);
        }

        void _compareWindow_ClosedWindow(object sender, ObjectEventArgs<FileImageViewModel, FileImageViewModel> e)
        {
            _compareWindow = null;
            _dupeRunner.Clear();
            // ?? ShowNextDupe();
        }

        void _compareWindow_WantRemove(object sender, ObjectEventArgs<FileImageViewModel, FileImageViewModel> e)
        {
            _dupeRunner.RemoveDupe(_viewModel);
            _dupeRunner.ShowNextDupe(_compareWindow);
        }

        public void ViewFullImage(FileImageViewModel vm)
        {
            OriginalAndTransformedBitmap transform = new OriginalAndTransformedBitmap(vm.FileImage, _filters);
            vm = new FileImageViewModel(transform);

            if (_newWindow == null)
            {
                _newWindow = new FullViewerWindow()
                {
                    WindowStartupLocation = WindowStartupLocation.Manual,
                    WindowState = WindowState.Maximized
                };
                _newWindow.WantNextImage += new EventHandler(newWindow_WantNextImage);
                _newWindow.WantPreviousImage += new EventHandler(newWindow_WantPreviousImage);
                _newWindow.WantDeleteImage += new EventHandler(_newWindow_WantDeleteImage);
                _newWindow.Closed += new EventHandler(_newWindow_Closed);
            }
            _newWindow.Show();
            System.Drawing.Rectangle rect = Screen.PrimaryScreen.WorkingArea;
            _newWindow.Topmost = Settings.Values.ViewerIsTopmost;

            _newWindow.FileImageViewModel = vm;
            _newWindow.Left = rect.Left;
            _newWindow.Top = rect.Top;
            _newWindow.Width = rect.Width;
            _newWindow.Height = rect.Height;

            Dispatcher.BeginInvoke(new VoidDelegate(() =>
                {
                    _newWindow.Focus();
                    _newWindow.Activate();
                    _newWindow.FitToScreen();
                    _newWindow.fullView.scroller.Width = rect.Width;
                    _newWindow.fullView.scroller.Height = rect.Height;
                    _newWindow.fullView.scroller.ScrollToHome();
                }));
        }

        void _newWindow_WantDeleteImage(object sender, EventArgs e)
        {
            var oldImage = _newWindow.FileImageViewModel;
            var image = collectionView.FileImageCollectionViewModel.GetNext(_newWindow.FileImageViewModel.FileName);

            if (image != null)
            {
                ViewFullImage(image);
            }
            else
            {
                image = collectionView.FileImageCollectionViewModel.GetPrevious(_newWindow.FileImageViewModel.FileName);

                if (image != null)
                {
                    ViewFullImage(image);
                }
            }

            _viewModel.Delete(oldImage);
            Console.WriteLine("Deleted " + oldImage.FileName);
        }

        void _newWindow_Closed(object sender, EventArgs e)
        {
            _newWindow.WantNextImage -= newWindow_WantNextImage;
            _newWindow.WantPreviousImage -= newWindow_WantPreviousImage;
            _newWindow.Closed -= _newWindow_Closed;
            _newWindow.WantDeleteImage -= _newWindow_WantDeleteImage;
            _newWindow = null;
        }

        void newWindow_WantPreviousImage(object sender, EventArgs e)
        {
            var image = collectionView.FileImageCollectionViewModel.GetPrevious(_newWindow.FileImageViewModel.FileName);

            if (image != null)
                ViewFullImage(image);
        }

        void newWindow_WantNextImage(object sender, EventArgs e)
        {
            var image = collectionView.FileImageCollectionViewModel.GetNext(_newWindow.FileImageViewModel.FileName);

            if (image != null)
                ViewFullImage(image);
        }

        protected void ViewFullImage(object sender, EventArgs e)
        {
            var images = collectionView.GetSelectedImages();

            if (images.Count == 0)
                return;

            ViewFullImage(images[0]);
        }

        protected void WantView(object sender, ObjectEventArgs<FileImageViewModel> args)
        {
            ViewFullImage(args.obj);
        }

        protected void WantCompare(object sender, EventArgs args)
        {
            var images = collectionView.GetSelectedImages();

            if (images.Count < 2)
                return;

            var image1 = images[0];
            var image2 = images[1];

            double comparison = image1.FileImage.Similarity(image2.FileImage);
            System.Windows.MessageBox.Show(comparison.ToString("N2"));
        }

        private void Window_Closing(object sender, CancelEventArgs e)
        {
            Settings.Values.ReadSettings(this);
            Settings.Values.SaveSettings(_settingsPath);
            _dirsViewModel.Save(_pathToExaminePath);
            _filterCollection.Save(_filterSave);
        }

        private void collectionView_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            collectionView.ForceRefresh = true;
        }

        private void ChangeThumbnailSize(object sender, ObjectEventArgs<double, double> e)
        {
            _settings.ThumbWidth = e.obj1;
            _settings.ThumbHeight = e.obj2;
            this._viewModel.InvalidateAllThumbnails();
        }
    }

    public class DirectoryToExamineViewModel : INotifyPropertyChanged
    {
        protected DirectoryToExamine _dir;

        public DirectoryToExamineViewModel(DirectoryToExamine dir)
        {
            _dir = dir;
            _dir.PropertyChanged += new PropertyChangedEventHandler(_dir_PropertyChanged);
        }

        void _dir_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            OnPropertyChanged(e.PropertyName);
        }

        public string Directory
        {
            get { return _dir.Directory; }
        }


        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

    public class DirectoriesToExamineViewModel : ObservableCollection<DirectoryToExamineViewModel>
    {
        protected DirectoriesToExamine _dirsToExamine;

        public void Refresh()
        {
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        public DirectoriesToExamineViewModel() { _dirsToExamine = new DirectoriesToExamine(); }
        public DirectoriesToExamineViewModel(DirectoriesToExamine dirs)
        {
            _dirsToExamine = dirs;
            foreach (var dir in dirs)
            {
                var vm = new DirectoryToExamineViewModel(dir);
                Add(vm);
            }
        }

        public void RemoveDir(string s)
        {
            var dir = _dirsToExamine.FirstOrDefault((d) => d.Directory.Equals(s, StringComparison.OrdinalIgnoreCase));
            if (dir != null)
            {
                _dirsToExamine.Remove(dir);
            }

            var dirVM = this.FirstOrDefault((vm) => vm.Directory.Equals(s, StringComparison.OrdinalIgnoreCase));

            if (dirVM != null)
            {
                Remove(dirVM);
            }
        }

        public void AddDir(string s)
        {
            DirectoryToExamine dir = new DirectoryToExamine(s);
            var vm = new DirectoryToExamineViewModel(dir);
            _dirsToExamine.Add(dir);
            Add(vm);
        }

        public void Save(string pathToExaminePath)
        {
            _dirsToExamine.Save(pathToExaminePath);
        }
    }

    public class DirectoriesToExamine : ObservableCollection<DirectoryToExamine>
    {
        public DirectoriesToExamine() { }

        public DirectoriesToExamine(IEnumerable<string> dirs)
        {
            foreach (var dir in dirs)
            {
                Add(new DirectoryToExamine(dir));
            }
        }

        public string[] GetDirectoryNames()
        {
            string[] dirNames = new string[this.Count];

            for (int n = 0; n < Count; n++)
            {
                dirNames[n] = this[n].Directory;
            }

            return dirNames;
        }

        public DirectoriesToExamine(IEnumerable<DirectoryToExamine> dirs)
        {
            foreach (var dir in dirs)
            {
                Add(dir);
            }
        }

        public void Save(string pathToExaminePath)
        {
            var dirNames = GetDirectoryNames();
            byte[] bytes = dirNames.SerializeToCompressedBinary();
            File.WriteAllBytes(pathToExaminePath, bytes);
        }

        public static string[] Load(string _pathToExaminePath)
        {
            if (File.Exists(_pathToExaminePath))
            {
                byte[] bytes = File.ReadAllBytes(_pathToExaminePath);
                string[] results = bytes.DeserializeFromCompressedBinary() as string[];
                return results;
            }

            return null;
        }
    }

    public class DirectoryToExamine : INotifyPropertyChanged
    {
        protected string _directory;

        public string Directory
        {
            get
            {
                return _directory;
            }
            set
            {
                if (_directory != value)
                {
                    _directory = value;
                    OnPropertyChanged("Directory");
                }
            }
        }

        public DirectoryToExamine() { }

        public DirectoryToExamine(string dir)
        {
            _directory = dir;
        }


        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
